昨天我們已經完成掃描 QRCode 並顯示購物清單的功能,但是當遇到不同編碼的 QRCode 資料時(例如 Big5 和 Base64),會出現問題。今天我們的目標就是處理這些不同的編碼並將其正確轉換為 UTF-8,讓 App 可以正確顯示商品明細。
今天的主要目標是:
Big5 是傳統的中文編碼方式,主要用於台灣。在掃描到發票 QRCode 的資料後,如果是 Big5 編碼,我們需要將其轉換為 UTF-8,這樣才能正確顯示商品名稱等資訊。
首先,我們來實作一個解碼 Big5 的函數:
func decodeBig5String(_ input: String) -> String {
let cfEncoding = CFStringEncoding(CFStringEncodings.big5.rawValue)
let encoding = String.Encoding(rawValue: CFStringConvertEncodingToNSStringEncoding(cfEncoding))
if let data = input.data(using: encoding) {
return String(data: data, encoding: .utf8) ?? input
}
return input // 如果無法解碼,返回原始字串
}
這段程式碼利用了 CFStringEncodings.big5 來處理 Big5 編碼。透過 CFStringConvertEncodingToNSStringEncoding
,我們可以將 Big5 轉換為能夠處理的字串編碼,並最終將其轉換為 UTF-8 進行顯示。
參考資料:
Base64 通常用於對二進制資料進行編碼,而在發票的 QRCode 資料中,有時候商品資訊可能會以 Base64 編碼的形式存在。
我們來實作一個解碼 Base64 的函數:
func decodeBase64String(_ input: String) -> String {
if let data = Data(base64Encoded: input) {
return String(data: data, encoding: .utf8) ?? input
}
return input // 如果無法解碼,返回原始字串
}
這裡我們使用了 Data(base64Encoded:)
將 Base64 編碼轉換為二進制資料,接著再將其轉換為 UTF-8 字串。這樣我們就能正確解碼 Base64 資料。
接著,我們需要更新 QRCode 的解析邏輯,判斷發票資訊的編碼類型,並根據不同的編碼進行解碼操作。
// 解析 QRCode 的函數
func parseQRCode(_ qrCode: String) -> [ScannedItem] {
var items: [ScannedItem] = []
// 檢查 QRCode 是否為右邊的格式(以 ** 開頭)
if qrCode.hasPrefix("**") {
let rightPart = String(qrCode.dropFirst(2))
let encodingType = getEncodingType(from: qrCode)
items = parseProductDetails(from: rightPart, encodingType: encodingType)
} else if qrCode.count > 95 {
// 左邊的 QRCode 從第 95 個字元開始解析商品資訊
let leftPart = String(qrCode.dropFirst(95))
// 取得編碼參數 (第 3 個欄位)
let encodingType = getEncodingType(from: qrCode)
items = parseProductDetails(from: leftPart, encodingType: encodingType)
}
return items
}
// 判斷編碼類型的輔助函數
func getEncodingType(from qrCode: String) -> String.Encoding {
let components = qrCode.components(separatedBy: ":")
if components.count >= 3, let encodingParam = Int(components[4]) {
switch encodingParam {
case 0:
return String.Encoding(rawValue: CFStringConvertEncodingToNSStringEncoding(CFStringEncoding(CFStringEncodings.big5.rawValue)))
case 1:
return .utf8 // UTF-8 編碼
case 2:
return .ascii // Base64 編碼(會進行進一步處理)
default:
return .utf8 // 預設為 UTF-8 編碼
}
}
return .utf8 // 預設為 UTF-8 編碼
}
// 解析商品明細的輔助函數
func parseProductDetails(from details: String, encodingType: String.Encoding) -> [ScannedItem] {
var items: [ScannedItem] = []
// 根據編碼類型處理商品名稱
let decodedDetails = decodeString(details, encodingType: encodingType)
// 使用 ":" 分隔商品資訊
let components = decodedDetails.components(separatedBy: ":")
// 每 3 個為一組:品名:數量:單價
for i in stride(from: 0, to: components.count, by: 3) {
if i + 2 < components.count {
let name = components[i] // 商品名稱
if let count = Int(components[i + 1]), // 數量
let price = Double(components[i + 2]) { // 單價
let item = ScannedItem(name: name, quantity: count, price: price)
items.append(item)
}
}
}
return items
}
// 根據編碼類型解碼字串
func decodeString(_ input: String, encodingType: String.Encoding) -> String {
if encodingType == .ascii, let data = Data(base64Encoded: input) {
// Base64 解碼
return String(data: data, encoding: .utf8) ?? input
} else if encodingType == String.Encoding(rawValue: CFStringConvertEncodingToNSStringEncoding(CFStringEncoding(CFStringEncodings.big5.rawValue))) {
// Big5 解碼
let big5Encoding = CFStringConvertEncodingToNSStringEncoding(CFStringEncoding(CFStringEncodings.big5.rawValue))
if let data = input.data(using: String.Encoding(rawValue: big5Encoding)),
let decodedString = String(data: data, encoding: String.Encoding(rawValue: big5Encoding)) {
return decodedString
}
} else if let data = input.data(using: encodingType), let decodedString = String(data: data, encoding: encodingType) {
// 其他編碼類型解碼 (如 UTF-8)
return decodedString
}
return input // 如果無法解碼,返回原字串
}
在這裡,我們根據 QRCode 中的編碼參數,動態選擇不同的解碼方式,讓 App 可以解讀不同的編碼資訊。
雖然我們今天加上對於 Big5 和 Base64 編碼的支援,並成功將它們轉換為 UTF-8,將掃描到的發票 QRCode 資料顯示出來,但其實還是有些問題。
最主要的問題是 QRCode 容量有限。以 電子發票證明聯一維及二維條碼規格說明 的範例就可以得知,在 Base64 編碼顯示商品名稱時,後續的數量與價格有可能會被放置到右邊的 QRCode,導致解析失敗。又或者 QRCode 無法將所有商品都列出等等,造成資料的不完整。而且以規格說明文件來看,商品明細並無強制規定要全部列出。
這些問題也許未來可以透過串接電子發票整合服務平台提供的 API 查詢發票和優化掃描 QRCode 的程式可以解決,本次練習就不考慮這些因素了。
明天我們將繼續實作將取得的商品列表存入Core Data中,敬請期待~